package com.hnx.admin.aurora.core;

import com.hnx.admin.aurora.core.api.BuildSqlFactory;
import com.hnx.admin.aurora.core.api.BuildSqlStrategy;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.transaction.Transaction;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

/*
创建人： 倔强的头发
创建时间：2025/7/3
功能描述:

*/
@Component
@RequiredArgsConstructor
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class AdditionalUpdateInterceptor implements Interceptor {
    private final BuildSqlFactory sqlProcessFactory;
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        lock.lock();
        try {
            //获取原始参数
            MappedStatement originalMs = (MappedStatement) invocation.getArgs()[0];
            Object parameter = invocation.getArgs()[1];
            Object result = invocation.proceed();
            SqlCommandType sqlCommandType = originalMs.getSqlCommandType();
            Executor executor = (Executor) invocation.getTarget();
            String mapperId = originalMs.getId();
            int index = mapperId.lastIndexOf(".");
            Class<?> mapperClass = Class.forName(mapperId.substring(0, index));
            String methodName = mapperId.substring(index + 1);
            Method method = findMethod(mapperClass, methodName);
            if (Objects.nonNull(method)) {
                BuildSqlStrategy strategy = sqlProcessFactory.getStrategy(sqlCommandType);
                SqlWithParams sqlWithParams = strategy.process(method, parameter);
                if (Objects.nonNull(sqlWithParams)) {
                    executeSql(sqlWithParams, executor);
                }
            }
            return result;
        }finally {
            lock.unlock();
        }
    }

    private void executeSql(SqlWithParams sqlWithParams, Executor executor) {
        if (Objects.nonNull(sqlWithParams)) {
            try {
                Transaction transaction = executor.getTransaction();
                Connection connection = transaction.getConnection();
                PreparedStatement stmt = connection.prepareStatement(sqlWithParams.sql);
                for (int i = 0; i < sqlWithParams.params.length; i++) {
                    stmt.setObject(i + 1, sqlWithParams.params[i]);
                }
                stmt.executeUpdate();
            } catch (Exception ex) {
                throw new RuntimeException(ex.getMessage());
            }
        }
    }

    private Method findMethod(Class<?> clazz, String methodName) {
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
}
